//Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace Microsoft.WindowsAPICodePack.Controls.WindowsForms
{
   /// <summary>
   /// Implements a CommandLink button that can be used in 
   /// WinForms user interfaces.
   /// </summary>    
   public class CommandLink : Button
   {
      /// <summary>
      /// Gets a System.Windows.Forms.CreateParams on the base class when 
      /// creating a window.
      /// </summary>        
      protected override CreateParams CreateParams
      {
         get
         {
            // Add BS_COMMANDLINK style before control creation.
            CreateParams cp = base.CreateParams;

            cp.Style = AddCommandLinkStyle(cp.Style);

            return cp;
         }
      }

      // Let Windows handle the rendering.
      /// <summary>
      /// Creates a new instance of this class.
      /// </summary>
      public CommandLink()
      {
         //CoreHelpers.ThrowIfNotVista();

         FlatStyle = FlatStyle.System;
      }

      // Add Design-Time Support.

      /// <summary>
      /// Increase default width.
      /// </summary>
      protected override System.Drawing.Size DefaultSize
      {
         get { return new System.Drawing.Size(180, 60); }
      }

      /// <summary>
      /// Specifies the supporting note text
      /// </summary>
      [Category("Appearance")]
      [Description("Specifies the supporting note text.")]
      [BrowsableAttribute(true)]
      [DefaultValue("(Note Text)")]
      public string NoteText
      {
         get { return (GetNote(this)); }
         set
         {
            SetNote(this, value);
         }
      }

      /// <summary>
      /// Enable shield icon to be set at design-time.
      /// </summary>
      [Category("Appearance")]
      [Description("Indicates whether the button should be decorated with the security shield icon (Windows Vista only).")]
      [BrowsableAttribute(true)]
      [DefaultValue(false)]
      public bool UseElevationIcon
      {
         get { return (useElevationIcon); }
         set
         {
            useElevationIcon = value;
            SetShieldIcon(this, useElevationIcon);
         }
      }

      private bool useElevationIcon;

      #region Interop helpers

      /// <summary>
      /// Sends the specified message to a window or windows. The SendMessage function calls 
      /// the window procedure for the specified window and does not return until the window 
      /// procedure has processed the message. 
      /// </summary>
      /// <param name="windowHandle">Handle to the window whose window procedure will receive the message. 
      /// If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, 
      /// including disabled or invisible unowned windows, overlapped windows, and pop-up windows; 
      /// but the message is not sent to child windows.
      /// </param>
      /// <param name="message">Specifies the message to be sent.</param>
      /// <param name="wparam">Specifies additional message-specific information.</param>
      /// <param name="lparam">Specifies additional message-specific information.</param>
      /// <returns>A return code specific to the message being sent.</returns>        
      [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
      private static extern IntPtr SendMessage(
          IntPtr windowHandle,
          uint message,
          IntPtr wparam,
          IntPtr lparam
      );

      /// <summary>
      /// Sends the specified message to a window or windows. The SendMessage function calls 
      /// the window procedure for the specified window and does not return until the window 
      /// procedure has processed the message. 
      /// </summary>
      /// <param name="windowHandle">Handle to the window whose window procedure will receive the message. 
      /// If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, 
      /// including disabled or invisible unowned windows, overlapped windows, and pop-up windows; 
      /// but the message is not sent to child windows.
      /// </param>
      /// <param name="message">Specifies the message to be sent.</param>
      /// <param name="wparam">Specifies additional message-specific information.</param>
      /// <param name="lparam">Specifies additional message-specific information.</param>
      /// <returns>A return code specific to the message being sent.</returns>
      [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
      private static extern IntPtr SendMessage(
         IntPtr windowHandle,
         uint message,
         IntPtr wparam,
         [MarshalAs(UnmanagedType.LPWStr)] string lparam);

      /// <summary>
      /// Sends the specified message to a window or windows. The SendMessage function calls 
      /// the window procedure for the specified window and does not return until the window 
      /// procedure has processed the message. 
      /// </summary>
      /// <param name="windowHandle">Handle to the window whose window procedure will receive the message. 
      /// If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, 
      /// including disabled or invisible unowned windows, overlapped windows, and pop-up windows; 
      /// but the message is not sent to child windows.
      /// </param>
      /// <param name="message">Specifies the message to be sent.</param>
      /// <param name="wparam">Specifies additional message-specific information.</param>
      /// <param name="lparam">Specifies additional message-specific information.</param>
      /// <returns>A return code specific to the message being sent.</returns>
      private static IntPtr SendMessage(
          IntPtr windowHandle,
          uint message,
          int wparam,
          string lparam)
      {
         return SendMessage(windowHandle, message, (IntPtr)wparam, lparam);
      }

      /// <summary>
      /// Sends the specified message to a window or windows. The SendMessage function calls 
      /// the window procedure for the specified window and does not return until the window 
      /// procedure has processed the message. 
      /// </summary>
      /// <param name="windowHandle">Handle to the window whose window procedure will receive the message. 
      /// If this parameter is HWND_BROADCAST, the message is sent to all top-level windows in the system, 
      /// including disabled or invisible unowned windows, overlapped windows, and pop-up windows; 
      /// but the message is not sent to child windows.
      /// </param>
      /// <param name="message">Specifies the message to be sent.</param>
      /// <param name="wparam">Specifies additional message-specific information.</param>
      /// <param name="lparam">Specifies additional message-specific information.</param>
      /// <returns>A return code specific to the message being sent.</returns>
      [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
      private static extern IntPtr SendMessage(
          IntPtr windowHandle,
          uint message,
          ref int wparam,
          [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lparam);

      private const int CommandLinkConst = 0x0000000E;
      private const uint SetNoteConst = 0x00001609;
      private const uint GetNoteConst = 0x0000160A;
      private const uint GetNoteLengthConst = 0x0000160B;
      private const uint SetShieldConst = 0x0000160C;

      private static int AddCommandLinkStyle(int style)
      {
         // Only add BS_COMMANDLINK style on Windows Vista or above.
         // Otherwise, button creation will fail.
         style |= CommandLinkConst;
         return style;
      }

      private static string GetNote(Button Button)
      {
         IntPtr retVal = SendMessage(
             Button.Handle,
             GetNoteLengthConst,
             IntPtr.Zero,
             IntPtr.Zero);

         // Add 1 for null terminator, to get the entire string back.
         int len = ((int)retVal) + 1;
         var strBld = new StringBuilder(len);

         SendMessage(Button.Handle, GetNoteConst, ref len, strBld);
         return strBld.ToString();
      }

      private static void SetNote(Button button, string text)
      {
         // This call will be ignored on versions earlier than Windows Vista.
         SendMessage(button.Handle, SetNoteConst, 0, text);
      }

      static internal void SetShieldIcon(Button Button, bool Show)
      {
         IntPtr fRequired = new IntPtr(Show ? 1 : 0);
         SendMessage(
            Button.Handle,
             SetShieldConst,
             IntPtr.Zero,
             fRequired);
      }

      #endregion
   }
}